package com.ilearn.proxy.custom;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GpProxy {

    private static String ln = "\r\n";


    public static Object newProxyInstance(GpClassLoader gpClassLoader, Class<?>[] interfaces, GpInvocationHandler h) {

        // 1. 动态生成源代码：.java。

        String src = generateSrc(interfaces);

        // 2. Java文件输出到磁盘。
        String filePath = GpProxy.class.getResource("").getPath();
        System.out.println("filePath:" + filePath);
        File f = new File(filePath + "$Proxy0.java");

        try {
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }


        // 3. 把生成的.java文件编译成.class文件。
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> iterable = manage.getJavaFileObjects(f);

        JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);

        task.call();

        try {
            manage.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


        // 4. 把编译生成的.class文件加载到JVM中。
        try {
            Class<?> proxyClass = gpClassLoader.findClass("$Proxy0");
            Constructor<?> constructor = proxyClass.getConstructor(GpInvocationHandler.class);
        // 5. 返回字节码重组以后的新的代理对象。
            return constructor.newInstance(h);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;

    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();

        sb.append("package com.ilearn.proxy.custom;" + ln);
        sb.append("import com.ilearn.proxy.custom.Person;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);


        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("GpInvocationHandler h;" + ln);
        sb.append("public $Proxy0(GpInvocationHandler h) {" + ln);
        sb.append("this.h = h;" + ln);
        sb.append("}" + ln);

        for (Method method : interfaces[0].getMethods()) {
            sb.append("public " + method.getReturnType().getName() + " " + method.getName() + "(){" + ln);
            sb.append("try {" + ln);
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\", new Class[]{});" + ln);
            sb.append("this.h.invoke(this, m, null);" + ln);
            sb.append("} catch (Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}" + ln);
            sb.append("}" + ln);
        }
        sb.append("}" + ln);


        return sb.toString();


    }
}
